package us.codecraft.webmagic.utils;

import com.alibaba.fastjson.JSONObject;
import org.apache.http.Header;
import org.apache.http.HttpHost;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.SocketTimeoutException;
import java.util.*;
import java.util.concurrent.TimeUnit;


public class HttpClientUtil {
    private static Logger log = LoggerFactory.getLogger(HttpClientUtil.class);

    private static final int connectionTimeout = 3000;
    private static final int socketTimeout = 5000;
    private static final PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager();
    private static CloseableHttpClient HTTP_CLIENT;


    static {
        // 设置最大连接数
        connManager.setMaxTotal(100);
        // 设置每个连接的路由数,根据请求qps配置
        connManager.setDefaultMaxPerRoute(10);
    }

    private static CloseableHttpClient getHttpClient() {
        if (HTTP_CLIENT == null) {
            synchronized (HttpClientUtil.class) {
                if (HTTP_CLIENT == null) {
                    RequestConfig requestConfig = RequestConfig.custom()
                            // 获取连接超时时间
                            .setConnectionRequestTimeout(5 * 1000)
                            // 请求超时时间
                            .setConnectTimeout(connectionTimeout)
                            // 响应超时时间
                            .setSocketTimeout(socketTimeout)
                            //.setProxy(new HttpHost("116.117.134.135",9999))
                            .build();

                    HTTP_CLIENT = HttpClients.custom()
                            // 把请求相关的超时信息设置到连接客户端
                            .setDefaultRequestConfig(requestConfig)
                            // 配置连接池管理对象
                            .setConnectionManager(connManager)
                            /*.setKeepAliveStrategy((response, context) -> {
                                HeaderElementIterator it = new BasicHeaderElementIterator
                                        (response.headerIterator(HTTP.CONN_KEEP_ALIVE));
                                while (it.hasNext()) {
                                    HeaderElement he = it.nextElement();
                                    String param = he.getName();
                                    String value = he.getValue();
                                    if (value != null && param.equalsIgnoreCase
                                            ("timeout")) {
                                        log.debug("get Keep-Alive from response:{}", value);
                                        return Long.parseLong(value) * 1000;
                                    }
                                }
                                return 600 * 1000;
                            })*/
                            .build();

                    //打印连接池状态
      /*              new Thread(() -> {
                        while (true) {
                              *//*  Set<HttpRoute> routes = connManager.getRoutes();
                                routes.forEach(e -> {
                                    PoolStats stats = connManager.getStats(e);
                                    log.info("【PoolingHttpClient ConnManager Stats】Per route:" + routes.toString() + stats.toString());
                                });*//*
                            // 获取所有路由的连接池状态
                            PoolStats totalStats = connManager.getTotalStats();
                            log.info("【PoolingHttpClient ConnManager Stats】Total status:" + totalStats.toString());
                            try {
                                Thread.sleep(10 * 1000);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }).start();*/

                    //释放闲置连接
                    //new IdleConnectionMonitorThread(connManager).start();
                }
            }
        }
        return HTTP_CLIENT;
    }

    public static String getCookie(Map<String, String> cookieMap) {
        StringBuilder sBuilder = new StringBuilder();
        for (Map.Entry<String, String> entry : cookieMap.entrySet()) {
            sBuilder.append(entry.getKey() + "=" + entry.getValue()).append(";");
        }
        if (sBuilder.length() > 1) {
            sBuilder.deleteCharAt(sBuilder.length() - 1);
        }
        return sBuilder.toString();
    }

    public static void main(String[] args) throws Exception {
        JSONObject json = JSONObject.parseObject("{\"marketplaceId\":\"ATVPDKIKX0DER\",\"localCountryCode\":\"US\"," +
                "\"customerId\":null,\"sessionId\":\"147-4780447-5354029\",\"deviceType\":\"DESKTOP\"," +
                "\"referrer\":\"/\",\"url\":\"/\",\"pageType\":\"Gateway\",\"languageOfPreference\":\"zh_CN\"," +
                "\"queryParams\":{},\"weblabTreatmentMap\":{\"CROSS_BORDER_INTERSTITIAL_ES_US_340017\":\"C\"," +
                "\"NARX_INTERSTITIAL_BUTTON_BACKGROUND_HIGHLIGHT_329086\":\"C\"," +
                "\"NARX_INTERSTITIAL_USE_SPECIAL_TEXT_PD_357221\":\"T1\",\"CROSS_BORDER_INTERSTITIAL_MX_US_341718\":\"C\"," +
                "\"NARX_INTERSTITIAL_ENABLE_CORAL_CLIENT_TIMEOUT_355189\":\"C\",\"NARX_INTERSTITIAL_MOBILE_DP_REDIRECTION_336249\":" +
                "\"T1\",\"CBI_355055\":\"C\",\"CROSS_BORDER_INTERSTITIAL_EG_302080\":\"C\"," +
                "\"NARX_INTERSTITIAL_DP_REDIRECTION_320915\":\"T1\",\"NARX_GOLDBOX_REDIRECTION_319969\":\"T1\",\"CROSS_BORDER_INTERSTITIAL_SEARCH_REDIRECTION_355157\":\"C\",\"NARX_INTERSTITIAL_LOCAL_CONFIG_354049\":\"T1\",\"CROSS_BORDER_INTERSTITIAL_FR_CA_332704\":\"C\"}}");


        Random random = new Random();
        String cookie = "";
        //主页
        Map<String, String> cookieMap = doGetForCookie("https://www.amazon.com", cookie);
        json.put("sessionId",cookieMap.get("session-id"));
        Thread.sleep(1000 + random.nextInt(1000));
        cookie= getCookie(cookieMap);
        System.out.println("amazon home:" + cookie);

        //https://www.amazon.com/cross_border_interstitial/render
        cookieMap.putAll(doPostWithJsonBody("https://www.amazon.com/cross_border_interstitial/render", cookie, json.toJSONString()));
        Thread.sleep(1000 + random.nextInt(1000));
        cookie= getCookie(cookieMap);
        System.out.println("cross_border_interstitial/render:" + cookie);

        //get-toaster
        cookieMap.putAll(doGetForCookie("https://www.amazon.com/gp/glow/get-toaster.html?pageType=Gateway&aisTransitionState=in&rancorLocationSource=IP_GEOLOCATION&_="+System.currentTimeMillis()
                , cookie));
        cookie= getCookie(cookieMap);
        System.out.println("get-toaster:" + cookie);
        Thread.sleep(1000 + random.nextInt(1000));

        //get-address-selections
        cookieMap.putAll(doGetForCookie("https://www.amazon.com/gp/glow/get-address-selections.html?deviceType=desktop&pageType=Gateway&storeContext=NoStoreName&actionSource=desktop-modal"
                , cookie));
        cookie = getCookie(cookieMap);
        System.out.println("get-address-selections:" + cookie);
        Thread.sleep(1000 + random.nextInt(1000));


        Map<String, String> params = new HashMap<String, String>();
        params.put("locationType", "LOCATION_INPUT");
        params.put("zipCode", "10010");
        params.put("storeContext", "generic");
        params.put("deviceType", "web");
        params.put("pageType", "Gateway");
        params.put("actionSource", "glow");
        params.put("almBrandId", "undefined");

        //https://www.amazon.com/gp/delivery/ajax/address-change.html
        cookieMap.putAll(doPostForCookie("https://www.amazon.com/gp/delivery/ajax/address-change.html", params, cookie.toString()));
        System.out.println(">>>>>>>cookie:" + getCookie(cookieMap));
    }

    public static Map<String, String> doPostForCookie(String url, Map<String, String> params, String cookies) {
        Map<String,String> cookieMap = new HashMap<String, String>();
        CloseableHttpResponse response = null;
        StringBuilder sBuilder = new StringBuilder();
        try {
            HttpPost httpPost = new HttpPost(url);
            httpPost.addHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
            httpPost.addHeader("Accept-Language", "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3");
            httpPost.setHeader("user-agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36");
            if (cookies != null && !cookies.isEmpty()) {
                //System.err.println("setCookie:" + cookies);
                httpPost.setHeader("cookie", cookies);
            }
            List<NameValuePair> parameters = new ArrayList<NameValuePair>(0);
            for (Map.Entry<String, String> entry : params.entrySet()) {
                parameters.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
            }
            // 构造一个form表单式的实体
            UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(parameters);
            // 将请求实体设置到httpPost对象中
            httpPost.setEntity(formEntity);
            response = getHttpClient().execute(httpPost);
            if (response.getStatusLine().getStatusCode() == 200) {
                String content = EntityUtils.toString(response.getEntity(), "UTF-8");
                System.out.println(">>>>>>>content:" + content);
            }


            for (Header header : response.getAllHeaders()) {
                if ("Set-Cookie".equalsIgnoreCase(header.getName())) {
                    String[] cookie = header.getValue().split(";")[0].split("=");
                    cookieMap.put(cookie[0], cookie[1]);
                    //sBuilder.append(cookie[0] + "=" + cookie[1]).append(";");
                }
            }
            if (sBuilder.length() > 0) {
                sBuilder.deleteCharAt(sBuilder.length() - 1);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (null != response) {
                try {
                    EntityUtils.consume(response.getEntity());
                } catch (IOException e) {
                    log.error("释放链接错误 {}", e);
                }
            }
        }
        System.out.println("========================");
        return cookieMap;
    }



    public static  Map<String,String> doGetForCookie(String url, String cookies) {
        Map<String,String> cookieMap = new HashMap<String, String>();
        CloseableHttpResponse response = null;
        StringBuilder sBuilder = new StringBuilder();
        try {
            HttpGet httpGet = new HttpGet(url);
            httpGet.addHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
            httpGet.addHeader("Accept-Language", "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3");
            httpGet.setHeader("user-agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36");
            if (cookies != null && !cookies.isEmpty()) {
                //System.err.println("setCookie:" + cookies);
                httpGet.setHeader("cookie", cookies);
            }
            response = getHttpClient().execute(httpGet);
            //String content = EntityUtils.toString(response.getEntity(), "UTF-8");
            for (Header header : response.getAllHeaders()) {
                if ("Set-Cookie".equalsIgnoreCase(header.getName())) {
                    String[] cookie = header.getValue().split(";")[0].split("=");
                    System.err.println(url+"  Set-Cookie:"+cookie[0]+"="+cookie[1]);
                    cookieMap.put(cookie[0], cookie[1]);
                    //sBuilder.append(cookie[0] + "=" + cookie[1]).append(";");
                }
            }
            if (sBuilder.length() > 0) {
                sBuilder.deleteCharAt(sBuilder.length() - 1);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (null != response) {
                try {
                    EntityUtils.consume(response.getEntity());
                } catch (IOException e) {
                    log.error("释放链接错误 {}", e);
                }
            }
        }
        System.out.println("========================");
        return cookieMap;
    }

    public static Map<String, String> doPostWithJsonBody(String url, String cookies, String jsonBody) {
        CloseableHttpResponse response = null;
        Map<String, String> cookieMap = new HashMap<String, String>();
        try {
            HttpPost post = new HttpPost(url);
            post.setHeader("Content-Type", "application/json");
            post.addHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
            post.addHeader("Accept-Language", "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3");
            post.setHeader("user-agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36");
            if (cookies != null && !cookies.isEmpty()) {
                //System.err.println("setCookie:" + cookies);
                post.setHeader("cookie", cookies);
            }
            StringEntity postBody = new StringEntity(jsonBody, "UTF-8");
            post.setEntity(postBody);
            response = getHttpClient().execute(post);
            String content = EntityUtils.toString(response.getEntity(), "UTF-8");
            for (Header header : response.getAllHeaders()) {
                if ("Set-Cookie".equalsIgnoreCase(header.getName())) {
                    String[] cookie = header.getValue().split(";")[0].split("=");
                    System.err.println(url+"  Set-Cookie:"+cookie[0]+"="+cookie[1]);
                    cookieMap.put(cookie[0], cookie[1]);
                    //sBuilder.append(cookie[0] + "=" + cookie[1]).append(";");
                }
            }
        } catch (ConnectTimeoutException e1) {
            log.error("doPost connectTimeout,url:{} ", url, e1);
        } catch (SocketTimeoutException e2) {
            log.error("doPost readTimeout,url:{} ", url, e2);
        } catch (Exception e) {
            log.error("doPost error url:{},params:{}", url, jsonBody, e);
        } finally {
            if (null != response) {
                try {
                    EntityUtils.consume(response.getEntity());
                } catch (IOException e) {
                    log.error("释放链接错误 {}", e);
                }
            }
        }
        return cookieMap;
    }

    public static class IdleConnectionMonitorThread extends Thread {

        private final HttpClientConnectionManager connMgr;
        private volatile boolean shutdown;

        public IdleConnectionMonitorThread(HttpClientConnectionManager connMgr) {
            super();
            this.connMgr = connMgr;
        }

        @Override
        public void run() {
            try {
                while (!shutdown) {
                    synchronized (this) {
                        wait(5000);
                        // Close expired connections
                        connMgr.closeExpiredConnections();
                        // 关闭闲置连接
                        connMgr.closeIdleConnections(5, TimeUnit.MINUTES);
                    }
                }
            } catch (InterruptedException ex) {
                // terminate
            }
        }

        public void shutdown() {
            shutdown = true;
            synchronized (this) {
                notifyAll();
            }
        }

    }
}
